package com.hjb.lock;

import com.hjb.result.Result;
import com.hjb.result.RetCode;
import com.hjb.result.RetModel;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * 分布式锁注解aop处理
 * 通过com.hjb.lock.Lock注解配置在类和方法使用，可以配置锁的超时时间、业务编号
 */
@Aspect
@Component
public class LockAspect {

    private static final Logger logger = LoggerFactory.getLogger(LockAspect.class);

    @Pointcut("@annotation(Lock)")
    public void targets() {
    }

    // 环绕方法
    @Around("targets()")
    public RetModel<Object> doAroundMethod(ProceedingJoinPoint pjp) {
        MethodSignature joinPointObject = (MethodSignature) pjp.getSignature();
        Method method = joinPointObject.getMethod();
        logger.debug("进入分布式锁，执行方法{}", method.toGenericString());
        Lock lock = method.getAnnotation(Lock.class);
        String lockId = lock.lockId();
        RetModel<Object> retModel = tryLockAndExecute(lockId, lock, pjp);
        if(retModel.getCode()!= RetCode.SUCCESS.code){
            throw new RuntimeException();
        }
        return retModel;
    }

    private RetModel<Object> tryLockAndExecute(String lockId, Lock lock, ProceedingJoinPoint pjp) {
        return (RetModel<Object>) LockTool.execute(lockId, lock.timeout(), new Callback() {
            @Override
            public RetModel<Object> onTimeout() {
                String timeOutMethodName = lock.timeOutMethodName();
                if (timeOutMethodName != null) {
                    return execute(timeOutMethodName);
                }
                return Result.buildErr("锁超时!");
            }

            @Override
            public RetModel<Object> onGetLock() {
                Object data = null;
                try {
                    data = pjp.proceed();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
                return Result.buildOk(data);
            }

            @Override
            public RetModel<Object> onError(Exception exception) {
                String errorMethodName = lock.errorMethodName();
                if (errorMethodName != null) {
                    return execute(errorMethodName);
                }
                return Result.buildErr("锁错误!");
            }
        });
    }

    private RetModel<Object> execute(String serviceName) {
        String str1 = serviceName.substring(0, serviceName.indexOf("("));
        String className = str1.substring(0, str1.lastIndexOf("."));
        String methodName = str1.substring(str1.lastIndexOf(".") + 1);
        String args = serviceName.substring(serviceName.lastIndexOf("(") + 1, serviceName.indexOf(")"));
        try {
            Object result = null;
            Class<?> clz = Class.forName(className);
            if (StringUtils.isEmpty(args)) {
                Method method = clz.getDeclaredMethod(methodName);
                result = method.invoke(clz.newInstance());
            } else {
                String[] argNames = args.split(",");
                Object[] argObjects = new Object[argNames.length];
                Class<?>[] argClasses = new Class[argNames.length];
                for (int i = 0; i < argClasses.length; i++) {
                    if (argNames[i].trim().length() > 0) {
                        argClasses[i] = Class.forName(argNames[i].trim());
                        argObjects[i] = argClasses[i].newInstance();
                    }
                }
                Method method = clz.getDeclaredMethod(methodName, argClasses);
                result = method.invoke(clz.newInstance(), argObjects);
            }
            return Result.buildOk(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
